/*--------------------------------------------------------------------------------------*\
**
** adv_obelisk.cpp
**
** Heroes IV
** Copyright 2000, The 3DO Company
**
\*--------------------------------------------------------------------------------------*/

// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "adv_obelisk.h"

#include "adv_object_type.h"
#include "adventure_frame.h"
#include "adventure_map.h"
#include "adventure_map_window.h"
#include "adventure_sounds.h"
#include "adventure_treasure_map.h"
#include "army.h"
#include "artifact_type.h"
#include "basic_dialog.h"
#include "convert_16_bit.h"
#include "decorative_adv_object_type.h"
#include "enum_operations.h"
#include "external_string.h"
#include "footprint.h"
#include "music.h"
#include "obelisk_marker.h"
#include "object_registration.h"
#include "paint_surface.h"
#include "pixel_24.h"
#include "player.h"
#include "puzzle_window.h"
#include "random.h"

/*
** Unnamed namespace
*/
namespace
{
    t_object_registration<t_adv_obelisk> k_registration( k_adv_object_obelisk );

    int const k_current_version = 1;
	
    int const k_default_treasure_radius = 4;

} // Unnamed namespace


/*
** Obelisk constructor.
*/
t_adv_obelisk::t_adv_obelisk (  std::string const& source )
         : t_stationary_adventure_object( source )
{
}

int t_adv_obelisk::get_version() const
{
	return k_current_version;
}

void t_adv_obelisk::initialize( t_adventure_map& map )
{
}

bool t_adv_obelisk::read( std::streambuf&						stream, 
						  t_qualified_adv_object_type const&	type,
						  int									version )
{
	if (version < 1)
    {
        return true;
    }

	int count		= ::get< t_uint32 >( stream );
	m_visited.resize( count );
	::read( stream, &(*m_visited.begin()), m_visited.size() );
    return true;
}

bool t_adv_obelisk::write( std::streambuf & stream ) const
{
    t_stationary_adventure_object::write( stream );

    // Visited.
	put< t_uint32 >( stream, m_visited.size() );
	::write( stream, &(*m_visited.begin()), m_visited.size() );

	return true;
}

void t_adv_obelisk::activate_trigger( t_army* army, t_adv_map_point const& point, 
		                               t_direction direction, t_adventure_frame* frame )
{
    int					owner_number;
    int					total_number_of_obelisks = 0;
    int					obelisks_visited = 0;
	t_adventure_map*	map_ptr = frame->get_map();

	t_obelisk_color		color = (t_obelisk_color)get_major_subtype();
    t_player*			player = army->get_owner();

    total_number_of_obelisks = map_ptr->get_required_obelisks ( color );

    // Obelisk info window.
    t_counted_ptr<t_basic_dialog> info_dialog_ptr;
    info_dialog_ptr = new t_basic_dialog;

	if (!(player->is_computer()))
    {
        // Did player get treasure already?
        if (map_ptr->get_obelisk_completed( color ))
        {
			info_dialog_ptr->set_text( get_text ("completed") );
            info_dialog_ptr->add_ok_button();
            info_dialog_ptr->open( get_dialog_bottom( point ), true, k_align_center );
	        info_dialog_ptr->run_modal();
            return;
        }
    }

	if ((owner_number = army->get_owner_number()) >= 0)
    {
        // Have we visited this obelisk yet?
		if (m_visited[owner_number] == 0)
        {
    		m_visited[owner_number] = 1;
            player->inc_obelisk_count( color );

            // Check if we need to place artifact - get number of obelisks visited by player.
            obelisks_visited = player->get_obelisk_count( color );

            // Visited all required obelisks and not placed already - then place artifact and show map.
            if (( obelisks_visited >= map_ptr->get_required_obelisks( color ) ) && (!(map_ptr->get_obelisk_treasure_placed( color ))))
            {
                // Place artifact.
                place_artifact ( map_ptr, army );
                map_ptr->set_obelisk_treasure_placed( color );
            }

			if (player->is_computer())
				return;

			info_dialog_ptr->set_text( get_text ("initial") );
			info_dialog_ptr->add_ok_button();
			info_dialog_ptr->open( get_dialog_bottom( point ), true, k_align_center );
			info_dialog_ptr->run_modal();
        }
        else
        {
            if (player->is_computer())
				return;

			info_dialog_ptr->set_text( get_text ("empty") );
            info_dialog_ptr->add_ok_button();
            info_dialog_ptr->open( get_dialog_bottom( point ), true, k_align_center );
	        info_dialog_ptr->run_modal();
            return;
        }
    }
    else
        return;

    // Reveal puzzle map.
	t_puzzle_window * puzzle_window_ptr = new t_puzzle_window( NULL, frame, player, color );

	t_sound_cache music = get_music_playing();
	stop_music();
	get_dialog_sound( k_dialog_sound_puzzle )->play( get_sound_volume() );
	puzzle_window_ptr->run_modal();
	play_music( music );
}

/*
** Find a location to place the artifact.
*/
void t_adv_obelisk::place_artifact( t_adventure_map * map_ptr, t_army * army )
{
    t_map_point_2d	        footprint_size;
    int                     i;
    t_level_map_point_2d	origin;
    t_map_point_2d		    offset;
	t_direction             direction;
    t_level_map_point_2d    map_point;
	t_obelisk_color color = (t_obelisk_color)get_major_subtype();
    std::vector< t_level_map_point_2d >	marker_points_list;
    std::vector< t_level_map_point_2d >	valid_points_list;
    std::vector< t_stationary_adventure_object * > dug_hole_object_list;
    std::vector< t_level_map_point_2d >	dug_hole_position_list;
    std::vector< int >	            radius_list;
    int                             radius;
    int                             half_radius;

    t_obelisk_marker_list marker_list = map_ptr->get_obelisk_marker_list( color );
    if (marker_list.size())
    {
        t_obelisk_marker_list::iterator marker_iterator = marker_list.begin();

        // Go through this list and get all locations.
        while ( marker_iterator != marker_list.end())
        {
            origin = marker_iterator->get_location();
            marker_points_list.push_back (origin);
        
            radius = marker_iterator->get_radius();
            radius_list.push_back (radius);

            marker_iterator++;
        }

        footprint_size = t_map_point_2d( 1, 1 );
    }
    // If there's no marker, use the location of the obelisk and the default radius.
    else
    {
        footprint_size = get_footprint().get_size();
        origin = map_ptr->get_object_pos( m_map_id );
        marker_points_list.push_back (origin);

        radius_list.push_back ( k_default_treasure_radius );
    }

    for (i=0; i<marker_points_list.size(); i++)
    {
        half_radius = (int)(((double)radius_list[i] + 0.5) / 2);

        /*
        ** First, get all valid points around object.
        */
        for (offset.row = -half_radius; offset.row < (footprint_size.row+half_radius); offset.row++)
       	{
       		for (offset.column = -half_radius; offset.column < (footprint_size.column+half_radius); offset.column++)
       		{
    			for (direction = t_direction(0); direction < k_direction_count; enum_incr( direction ))
                {
       				map_point = marker_points_list[i] + offset;

                    if ( !map_ptr->is_valid( map_point ) )
          				continue;
					if (map_ptr->is_ocean( map_point ))
						continue;

                    // Check distance.
                    double  distance_to_obelisk = distance ( map_point - marker_points_list[i] );

                    if ( distance_to_obelisk > radius_list[i] )
                        continue;
                    
                    // Check tile.
                    t_adventure_tile const& map_tile 
						= map_ptr->get_adv_tile( t_level_map_point_2d( map_point, 
												 marker_points_list[i].level ) );

					t_terrain_type terrain_type = map_tile.get_terrain();
					if ((terrain_type == k_terrain_lava) || (terrain_type == k_terrain_lava_river))
						continue;

					if (map_tile.blocks_army( *army, *map_ptr, false ))
						continue;

                    // If any intersecting objects, continue...
    			    int num_object_count = map_tile.get_intersecting_object_count();

					bool is_hole = false;

                    // Check to see if it's a hole...
    			    while (num_object_count-- > 0)
                    {
	                    t_adventure_map_object_id obj_id = map_tile.get_intersecting_object_id( num_object_count );

	                    t_adventure_object &object = map_ptr->get_adv_object( obj_id );
                        t_stationary_adventure_object * stationary_object = 
                                dynamic_cast<t_stationary_adventure_object *> (&object);

                        if (stationary_object)
                        {
                            t_qualified_adv_object_type obj_type = stationary_object->get_type();
	                        int	subtype = stationary_object->get_major_subtype();

                            // If it's a hole, add it, in case we need to remove a hole to
                            // place a treasure.
                            if (( obj_type.get() == k_adv_object_decorative ) && ( subtype == k_decorative_crack ))
                            {
                                t_level_map_point_2d hole_position = map_ptr->get_object_pos( obj_id );

                                dug_hole_object_list.push_back( stationary_object );
                                dug_hole_position_list.push_back( hole_position );
								is_hole = true;
								break;
                            }
                        }
                    }
	
					if (!is_hole)
						valid_points_list.push_back ( map_point );
                }
            }
        }
    }

    // No bury point found!
    if (valid_points_list.size() == 0)
    {
        // Pick a dig graphic to remove, if any.
        if (dug_hole_object_list.size())
        {
            int hole_index = random( dug_hole_object_list.size() );

            // Remove this dig graphic and place treasure here.
            dug_hole_object_list[ hole_index ]->destroy();
            valid_points_list.push_back ( dug_hole_position_list[ hole_index ] );
        }
        else
        {
            valid_points_list.push_back ( army->get_position() );
        }
    }
    
    // Now, select a point randomly from list.
    int point_index = random( valid_points_list.size() );

    // Set treasure position.
    map_ptr->set_obelisk_artifact_position(  color, t_level_map_point_2d( valid_points_list[ point_index ], 
                            valid_points_list[ point_index ].level ) );

}

void t_adv_obelisk::place( t_adventure_map& map, t_adv_map_point const& point )
{
    t_stationary_adventure_object::place( map, point );

    map.add_obelisk( *this );
}

float t_adv_obelisk::ai_value( t_adventure_ai const& ai, t_creature_array const& army, int move_cost ) const
{
	// I make it more valuable as I get obelisks.
	if (m_visited[army.get_owner_number()])
		return 0.0f;
	t_player const* player = army.get_owner();
	t_adventure_map const* map = army.get_map();
	t_obelisk_color color = static_cast<t_obelisk_color>(get_major_subtype());
	int required = map->get_required_obelisks( color );
	return player->ai_get_artifact_value( k_artifact_level_relic ) 
			/ (player->get_obelisk_count( color ) + 1);
}

